package com.free.mini.gateway.filter;

import java.nio.charset.StandardCharsets;

import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONValidator;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.free.mini.common.base.ResultCode;
import com.free.mini.common.base.ResultData;
import com.free.mini.common.utils.BizTraceUtil;
import com.free.mini.gateway.constants.GatewayConstant;
import com.free.mini.gateway.utils.PathWildcardMatchUtil;
import com.free.mini.gateway.utils.SignUtil;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class GlobalResponseFilter implements GlobalFilter, Ordered {
	/**
	 * 当前应用名称
	 */
	@Value("${spring.application.name:cc-gateway}")
	private String applicationName;
	/**
	 * 响应是否加密：true、false
	 */
	@Value("${nanopay.response.encode:false}")
	private boolean responseEncode;


	@Value("${skip.url.list:/api/admin/v1/upload/file}")
	private String skipUrlList;
	/**
	 * 不需要加密的URL列表
	 */
	@Value("${skip.encrypt.url.list:"
			//walletApi
			//+ "/walletApi/app/v1/agreement/generate"
			+ "}")
	String skipEncryptUrlList;
	@Value("${gateway.key.private}")
	private String privateKey;

	@Value("${gateway.aes.decrypt.iv}")
	private String iv;

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		try {
			ApplicationContext ctx = exchange.getApplicationContext();
			ServerHttpRequest request = exchange.getRequest();
			boolean skipUrl = PathWildcardMatchUtil.wildcardMatch(request.getURI().getPath(), skipUrlList);
			if (skipUrl) {
				return chain.filter(exchange);
			}
			if (request.getMethod() != HttpMethod.POST && request.getMethod() != HttpMethod.PUT
					&& request.getMethod() != HttpMethod.PATCH) {
				return chain.filter(exchange);
			}
			ServerHttpResponse originalResponse = exchange.getResponse();
			DataBufferFactory bufferFactory = originalResponse.bufferFactory();
			ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
				@SuppressWarnings("rawtypes")
				@Override
				public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
					String statusCode = originalResponse.getStatusCode().value() + "";
					/**
					 * 处理非200返回
					 */
					if (!statusCode.equals("200")) {
						GatewayContext gatewayContext = (GatewayContext) exchange.getAttributes()
								.get(GatewayConstant.CACHE_GATEWAY_CONTEXT);
						ResultData encodeResultData = new ResultData(ResultCode.SYSTEM_ERROR);
						gatewayContext.setResult(encodeResultData);

						String r = JSON.toJSONString(encodeResultData);
						byte[] bytes = r.getBytes(StandardCharsets.UTF_8);
						HttpHeaders headers = originalResponse.getHeaders();
						headers.remove(GatewayConstant.CONTENT_LENGTH);
						headers.setContentLength(bytes.length);
						headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
						return super.writeWith(DataBufferUtils.join(Mono.just(bufferFactory.wrap(bytes))));
					}
					/**
					 * 处理200返回
					 */
					if (!(body instanceof Flux)) {
						return super.writeWith(body);
					}
					return super.writeWith(DataBufferUtils.join(Flux.from(body)).map(dataBuffer -> {
						try {
							byte[] content = new byte[dataBuffer.readableByteCount()];
							dataBuffer.read(content);
							return content;
						} finally {
							DataBufferUtils.release(dataBuffer);
						}
					}).flatMap(content -> {
						ResultData encodeResultData = null;
						GatewayContext gatewayContext = (GatewayContext) exchange.getAttributes()
								.get(GatewayConstant.CACHE_GATEWAY_CONTEXT);
						boolean isFormatJson = true;
						try {
							String oriResultStr = new String(content);
							if (log.isDebugEnabled()) {
								log.debug("skipEncryptUrlList:{}", skipEncryptUrlList);
								log.info("响应报文长度:{}", oriResultStr.length());
								log.debug("响应报文:{}", oriResultStr);
							}
							// json 格式校验
							try {
								JSONValidator jsonValidator = JSONValidator.from(oriResultStr);
								isFormatJson = jsonValidator.validate();
							} catch (Exception e) {
								if (log.isDebugEnabled()) {
									log.info("it(' {} ') is not normal json format, return.", oriResultStr);
								}
								isFormatJson = false;
							}
							// 非json格式得响应直接返还 内容
							if (!isFormatJson) {
								return Mono.just(bufferFactory.wrap(oriResultStr.getBytes(StandardCharsets.UTF_8)));
							}
							ResultData resultGenerator = JSON.parseObject(oriResultStr, ResultData.class);
							if (resultGenerator == null) {
								resultGenerator = new ResultData(ResultCode.SYSTEM_ERROR);
							}

							if (resultGenerator.getData() != null) {
								encodeResultData = encrypt(resultGenerator);
							} else {
								encodeResultData = resultGenerator;
								gatewayContext.setResult(encodeResultData);
							}
						} catch (Exception e) {
							log.error("网关执行加密异常", e);
							encodeResultData = new ResultData(ResultCode.SYSTEM_ERROR);
							if (log.isDebugEnabled()) {
								log.debug("after encrypt :{}", encodeResultData);
							}
							gatewayContext.setResult(encodeResultData);
						}
						String r = JSON.toJSONString(encodeResultData, SerializerFeature.WriteMapNullValue);
						if (log.isDebugEnabled()) {
							log.debug("加密报文:{}", r);
						}
						byte[] bytes = r.getBytes(StandardCharsets.UTF_8);
						HttpHeaders headers = originalResponse.getHeaders();
						headers.remove(GatewayConstant.CONTENT_LENGTH);
						headers.setContentLength(bytes.length);
						headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
						return Mono.just(bufferFactory.wrap(bytes));
					}));
				}
			};
			return chain.filter(exchange.mutate().response(decoratedResponse).build());
		}catch (Exception e){
			e.printStackTrace();
		}
		return chain.filter(exchange);
	}
	private ResultData encrypt(ResultData resultData){
		try {
			if (!ResultData.isSuccess(resultData)){
				return null;
			}
			resultData.setSign(SignUtil.sign(JSON.toJSONString(resultData.getData(),SerializerFeature.MapSortField)));
			String key = SignUtil.generateAESKey();
			resultData.setRandomKey(SignUtil.rsaPrivateKeyEncrypt(privateKey, key));
			resultData.setData(SignUtil.aesEncrypt(iv,key,JSON.toJSONString(resultData.getData(),SerializerFeature.MapSortField)));
			resultData.setTraceId(BizTraceUtil.currentTraceId());
			return resultData;
		} catch (Exception e) {
			log.error("response encrypt 异常",e);
			return null;
		}
	}
	@Override
	public int getOrder() {
		return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
	}

}
